package org.nutz.el.arithmetic;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

import org.nutz.el.Operator;
import org.nutz.el.opt.arithmetic.LBracketOpt;
import org.nutz.el.opt.arithmetic.RBracketOpt;
import org.nutz.el.opt.logic.QuestionOpt;
import org.nutz.el.opt.logic.QuestionSelectOpt;
import org.nutz.el.parse.Converter;

/**
 * Shunting yard算法是一个用于将中缀表达式转换为后缀表达式的经典算法，由艾兹格·迪杰斯特拉引入，因其操作类似于火车编组场而得名。<br/>
 * 参考:
 * <a href='http://zh.wikipedia.org/wiki/Shunting_yard%E7%AE%97%E6%B3%95'>Shunting yard算法</a>
 * 
 * @author juqkai(juqkai@gmail.com)
 *
 */
public class ShuntingYard {
    private LinkedList<Operator> opts;
    private Queue<Object> rpn;
    
    /**
     * 转换操作符.
     * 根据 ShuntingYard 算法进行操作
     * @param current
     */
    private void parseOperator(Operator current){
        //空,直接添加进操作符队列
        if(opts.isEmpty()){
            opts.addFirst(current);
            return;
        }
        //左括号
        if(current instanceof LBracketOpt){
            opts.addFirst(current);
            return;
        }
        //遇到右括号
        if(current instanceof RBracketOpt){
            while(!opts.isEmpty() && !(opts.peek() instanceof LBracketOpt)){
                rpn.add(opts.poll());
            }
            opts.poll();
            return;
        }
        
        //符号队列top元素优先级大于当前,则直接添加到
        if(!opts.isEmpty() && opts.peek().fetchPriority() > current.fetchPriority()){
            opts.addFirst(current);
            return;
        }
        //一般情况,即优先级小于栈顶,那么直接弹出来,添加到逆波兰表达式中
        while(!opts.isEmpty() && opts.peek().fetchPriority() <= current.fetchPriority()){
            //三元表达式嵌套的特殊处理
            if(opts.peek() instanceof QuestionOpt && current instanceof QuestionOpt){
                break;
            }
            if(opts.peek() instanceof QuestionOpt && current instanceof QuestionSelectOpt){
                rpn.add(opts.poll());
                break;
            }
            rpn.add(opts.poll());
        }
        opts.addFirst(current);
    }
    
    /**
     * 转换成 逆波兰表示法（Reverse Polish notation，RPN，或逆波兰记法）
     * @param val
     * @throws IOException 
     */
    public Queue<Object> parseToRPN(String val) {
        rpn = new LinkedList<Object>();
        opts = new LinkedList<Operator>();
        
        Converter converter = new Converter(val);
        converter.initItems();
        while(!converter.isEnd()){
            Object item = converter.fetchItem();
            if(item instanceof Operator){
                parseOperator((Operator) item);
                continue;
            }
            rpn.add(item);
        }
        while(!opts.isEmpty()){
            rpn.add(opts.poll());
        }
        
        return rpn;
    }
}
